package cn.wolfcode.service.impl;

import cn.wolfcode.common.exception.BusinessException;
import cn.wolfcode.common.web.CodeMsg;
import cn.wolfcode.common.web.Result;
import cn.wolfcode.domain.OrderInfo;
import cn.wolfcode.domain.PayVo;
import cn.wolfcode.domain.SeckillProductVo;
import cn.wolfcode.fegin.AliPayFeignApi;
import cn.wolfcode.mapper.OrderInfoMapper;
import cn.wolfcode.mapper.PayLogMapper;
import cn.wolfcode.mapper.RefundLogMapper;
import cn.wolfcode.mq.MQConstant;
import cn.wolfcode.mq.OrderTimeoutMessage;
import cn.wolfcode.redis.SeckillRedisKey;
import cn.wolfcode.service.IOrderInfoService;
import cn.wolfcode.service.ISeckillProductService;
import cn.wolfcode.util.IdGenerateUtil;
import cn.wolfcode.web.msg.SeckillCodeMsg;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;

/**
 * Created by wolfcode
 */
@Service
@Slf4j
public class OrderInfoSeviceImpl implements IOrderInfoService {
    @Autowired
    private ISeckillProductService seckillProductService;
    @Autowired
    private OrderInfoMapper orderInfoMapper;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private PayLogMapper payLogMapper;
    @Autowired
    private RefundLogMapper refundLogMapper;
    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    @Override
    public OrderInfo selectByUserIdAndSeckillId(Long userId, Long seckillId, Integer time) {
        return orderInfoMapper.selectByUserIdAndSeckillId(userId, seckillId, time);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public String doSeckill(Long userId, SeckillProductVo vo) {
        // 1. 扣除秒杀商品库存
        int row = seckillProductService.decrStockCount(vo.getId());
        if (row == 0) {
            // 库存不足
            throw new BusinessException(SeckillCodeMsg.SECKILL_STOCK_OVER);
        }
        OrderInfo orderInfo = null;
        try {
            // 2. 创建秒杀订单并保存
            orderInfo = this.buildOrderInfo(userId, vo);
            orderInfoMapper.insert(orderInfo);
           // redisTemplate.opsForValue().set(SeckillRedisKey.SECKILL_ORDER_CACHE.join(orderInfo.getOrderNo()), JSON.toJSONString(orderInfo));
        } catch (Exception e) {
            // 如果保存时, 出现异常, 说明用户已经下过单, 库存就不应该 -1
            // 还原库存: 从数据库中查到真实库存, 最终还原回去
            String stockCountKey = SeckillRedisKey.SECKILL_STOCK_COUNT_HASH.join(vo.getTime() + "");
            vo = seckillProductService.queryByTimeAndSeckillId(vo.getTime(), vo.getId()); // 忽略缓存
            redisTemplate.opsForHash().put(stockCountKey, vo.getId() + "", vo.getStockCount());

            throw new BusinessException(SeckillCodeMsg.REPEAT_SECKILL);
        }
        // 3. 返回订单编号
        return orderInfo.getOrderNo();
    }

    @Override
    public OrderInfo findById(String orderNo) {
        String json = redisTemplate.opsForValue().get(SeckillRedisKey.SECKILL_ORDER_CACHE.join(orderNo));
        if (!StringUtils.isEmpty(json)){
            return JSON.parseObject(json, OrderInfo.class);
        }
        return orderInfoMapper.findById(orderNo);
    }

    @Override
    public void rollBackTran(Long userId, Long seckillId, Integer time) {
        //从数据库中查出来vo
        SeckillProductVo vo = seckillProductService.queryByTimeAndSeckillId(time, seckillId);
        log.info("[订单服务] 订单创建失败，回滚Redis ，userId={},seckillId={},stockCount={}", userId, seckillId, vo.getStockCount());
        //回滚Redis内存的订单数量的缓存
        redisTemplate.opsForHash().put(
                SeckillRedisKey.SECKILL_STOCK_COUNT_HASH.join(time + ""),
                seckillId + "",
                vo.getStockCount() + "");
        //回滚Redis内存中的订单数据的重复下单标识
        String key = SeckillRedisKey.SECKILL_ORDER_HASH.join(userId + "");
        redisTemplate.opsForHash().put(key, seckillId + "", "0");
        //发送消息 清除所有秒杀服务的 本地标识(集群模式)
        rocketMQTemplate.syncSend(MQConstant.CANCEL_SECKILL_OVER_SIGN_TOPIC, seckillId);
        log.info("[订单服务] 发送取消本地标识消息:{}", seckillId);
    }

    @Override
    public void checkOrderPayTimeout(OrderTimeoutMessage message) {
        //基于订单id 查询定单对象
        //判断订单状态修改为未支付，如果是未支付
        //订单状态修改为超时取消
        int ret = orderInfoMapper.updateCancelStatus(message.getOrderNo(), OrderInfo.STATUS_TIMEOUT);
        if (ret > 0) {
            log.info("[订单超时检查] 订单：{} ，已经超时，即将进行回滚库存操作...", message.getOrderNo());
            //mysql 秒杀商品库存 +1
            seckillProductService.incryStockCount(message.getTime(),message.getSeckillId());
            //从数据库中查出来vo
            SeckillProductVo vo = seckillProductService.queryByTimeAndSeckillId(message.getTime(), message.getSeckillId());
            log.info("[订单超时检查] 订单超时，回滚Redis ，userId={},seckillId={},stockCount={}", message.getUserPhone(), message.getSeckillId(), vo.getStockCount());
            //回滚Redis内存的订单数量的缓存
            redisTemplate.opsForHash().put(
                    SeckillRedisKey.SECKILL_STOCK_COUNT_HASH.join(message.getTime() + ""),
                    message.getSeckillId() + "",
                    vo.getStockCount() + "");
            //回滚Redis内存中的订单数据的重复下单标识
            String key = SeckillRedisKey.SECKILL_ORDER_HASH.join(message.getUserPhone() + "");
            redisTemplate.opsForHash().put(key, message.getSeckillId() + "", "0");
            //发送消息 清除所有秒杀服务的 本地标识(集群模式)
            rocketMQTemplate.syncSend(MQConstant.CANCEL_SECKILL_OVER_SIGN_TOPIC,message.getSeckillId());
            log.info("[订单超时检查] 发送取消本地标识消息:{}", message.getSeckillId());
        }
        //redis 库存回补
        //取消用户重复下单标识
        //取消本地标识
    }
    @Autowired
    private AliPayFeignApi aliPayFeignApi;

    @Override
    public String  aliPay(String orderNo, Long userId) {
        OrderInfo orderInfo = this.findById(orderNo);
        if (orderInfo==null||!orderInfo.getUserId().equals(userId)){
            throw new BusinessException(SeckillCodeMsg.PAY_SERVER_ERROR);
        }
        PayVo pay = new PayVo();
        pay.setOutTradeNo(orderNo);
        pay.setSubject("[限时抢购] "+orderInfo.getProductName());
        pay.setTotalAmount(orderInfo.getSeckillPrice().toString());
        Result<String> result = aliPayFeignApi.doPay(pay);
        if (result==null||result.hasError()){
            throw new BusinessException(new CodeMsg(result.getCode(),result.getMsg()));
        }

        return result.getData();
    }

    private OrderInfo buildOrderInfo(Long userId, SeckillProductVo vo) {
        Date now = new Date();
        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setCreateDate(now);
        orderInfo.setDeliveryAddrId(1L);
        orderInfo.setIntergral(vo.getIntergral());
        orderInfo.setOrderNo(IdGenerateUtil.get().nextId() + "");
        orderInfo.setPayType(OrderInfo.PAY_TYPE_ONLINE);
        orderInfo.setProductCount(1);
        orderInfo.setProductId(vo.getProductId());
        orderInfo.setProductImg(vo.getProductImg());
        orderInfo.setProductName(vo.getProductName());
        orderInfo.setProductPrice(vo.getProductPrice());
        orderInfo.setSeckillDate(now);
        orderInfo.setSeckillId(vo.getId());
        orderInfo.setSeckillPrice(vo.getSeckillPrice());
        orderInfo.setSeckillTime(vo.getTime());
        orderInfo.setStatus(OrderInfo.STATUS_ARREARAGE);
        orderInfo.setUserId(userId);
        return orderInfo;
    }
}
